namespace GomuLibrary.IO.DiscImage
{
    using GomuLibrary.Win32API;
    using Microsoft.Win32.SafeHandles;
    using System;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Threading;

    public class Iso9660Creator : IDisposable
    {
        private AutoResetEvent _autoEvent = new AutoResetEvent(false);
        private int _bufSize;
        private long _discLength;
        private string _discPath;
        private string _outputPath;
        private Thread _thWorks;
        public const int BUFFER_SIZE_MAX = 0x100000;
        public const int DEFAULT_BUFFER_SIZE = 0x20000;
        private bool disposed;
        private const uint FAT32_DISK_LIMIT = uint.MaxValue;
        private object thisLock = new object();

        public event EventHandler<Iso9660CreatorEventArgs> Aborted;

        public event EventHandler<Iso9660CreatorEventArgs> Progression;

        public event EventHandler Terminate;

        public void Abort()
        {
            this._autoEvent.Set();
        }

        private SafeFileHandle CreateFileDevice(string path)
        {
            string str = path.EndsWith(@"\") ? path.Substring(0, path.Length - 1) : path;
            return new SafeFileHandle(SafeNativeMethods.CreateFile(@"\\.\" + str, 0x80000000, 1, IntPtr.Zero, 3, 0x80, IntPtr.Zero), true);
        }

        public void CreateImage(string discPath, string output)
        {
            this.CreateImage(discPath, output, false, 0x20000, true);
        }

        public void CreateImage(string discPath, string output, bool newThread)
        {
            this.CreateImage(discPath, output, newThread, 0x20000, true);
        }

        public void CreateImage(string discPath, string output, int bufferSize)
        {
            this.CreateImage(discPath, output, false, bufferSize, true);
        }

        public void CreateImage(string discPath, string output, bool newThread, bool overwrite)
        {
            this.CreateImage(discPath, output, newThread, 0x20000, overwrite);
        }

        public void CreateImage(string discPath, string output, int bufferSize, bool overwrite)
        {
            this.CreateImage(discPath, output, false, bufferSize, overwrite);
        }

        public void CreateImage(string discPath, string output, bool newThread, int bufferSize, bool overwrite)
        {
            this._discPath = discPath;
            this._outputPath = output;
            this._bufSize = bufferSize;
            DriveInfo info = new DriveInfo(this._discPath);
            this._discLength = info.TotalSize;
            if (this._discPath == string.Empty)
            {
                throw new ArgumentNullException("discPath", "The cd/dvd drive path must not be null.");
            }
            if (this._outputPath == string.Empty)
            {
                throw new ArgumentNullException("output", "The output file path must not be null.");
            }
            if (info.DriveType != DriveType.CDRom)
            {
                throw new IOException("The cd/dvd drive specified is not a CDRom type.");
            }
            if (!info.IsReady)
            {
                throw new IOException("The cd/dvd drive specified is empty or is not ready.");
            }
            if (!overwrite && File.Exists(this._outputPath))
            {
                throw new IOException("The output file already exists.");
            }
            DriveInfo info2 = new DriveInfo(Path.GetPathRoot(output));
            if (info2.DriveFormat.Equals("FAT32") && (info.TotalSize > 0xffffffffL))
            {
                throw new IOException("Not enough space on FAT32 destination drive.");
            }
            if (info2.TotalSize < this._discLength)
            {
                throw new IOException("Not enough space on the destination drive.");
            }
            if (this._bufSize <= 0)
            {
                throw new ArgumentOutOfRangeException("bufferSize", "The buffer size is negative");
            }
            if (bufferSize > 0x100000)
            {
                throw new ArgumentOutOfRangeException("Buffer is too large.");
            }
            if (newThread)
            {
                if ((this._thWorks == null) || (this._thWorks.ThreadState != ThreadState.Running))
                {
                    this._thWorks = new Thread(new ThreadStart(this.ThWorks));
                    this._thWorks.Name = "ISO9660CreatorWorks";
                    this._thWorks.Start();
                }
            }
            else
            {
                this.ThWorks();
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Dispose(bool disposing)
        {
            if (!this.disposed && disposing)
            {
                this._autoEvent.Close();
            }
            this.disposed = true;
        }

        private void ThWorks()
        {
            lock (this.thisLock)
            {
                this._autoEvent.Reset();
                SafeFileHandle handle = this.CreateFileDevice(this._discPath);
                FileStream stream = null;
                FileStream stream2 = null;
                try
                {
                    if (handle.IsClosed)
                    {
                        throw new IOException("The device handle is closed.");
                    }
                    if (handle.IsInvalid)
                    {
                        throw new IOException("The device handle is invalid.");
                    }
                    stream = new FileStream(handle, FileAccess.Read, this._bufSize);
                    stream2 = new FileStream(this._outputPath, FileMode.Create, FileAccess.Write, FileShare.None, this._bufSize);
                    byte[] buffer = new byte[this._bufSize];
                    do
                    {
                        Iso9660CreatorEventArgs args;
                        if (this._autoEvent.WaitOne(0, false))
                        {
                            if (this.Aborted != null)
                            {
                                args = new Iso9660CreatorEventArgs(stream2.Position, this._discLength);
                                this.Aborted(null, args);
                                break;
                            }
                        }
                        else
                        {
                            int count = stream.Read(buffer, 0, buffer.Length);
                            stream2.Write(buffer, 0, count);
                            if (this.Progression != null)
                            {
                                args = new Iso9660CreatorEventArgs(stream2.Position, this._discLength);
                                this.Progression(new object(), args);
                            }
                        }
                    }
                    while (this._discLength != stream2.Position);
                    Array.Clear(buffer, 0, buffer.Length);
                    if (this.Terminate != null)
                    {
                        this.Terminate(null, EventArgs.Empty);
                    }
                }
                catch (Exception)
                {
                }
                finally
                {
                    if (stream != null)
                    {
                        stream.Close();
                        stream.Dispose();
                    }
                    if (stream2 != null)
                    {
                        stream2.Close();
                        stream2.Dispose();
                    }
                    handle.Close();
                    handle.Dispose();
                }
            }
        }
    }
}

